#ifndef __UDP_SERVER_HPP__   //防止头文件重复包含
#define __UDP_SERVER_HPP__

#include<iostream>
#include<string>
#include<memory>
#include<cstring>
#include<cerrno>
#include<strings.h>   // bzero

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h> // 网络通信
#include <arpa/inet.h>

#include"Log.hpp"
#include"Common.hpp"
#include"InetAddr.hpp"

using namespace LogModule;


const static int gsockfd = -1;
//const static std::string gdefaultip = "127.0.0.1"; // 表示本地主机
const static uint16_t gdefaultport = 8080;

class UdpServer
{
public:
	UdpServer(uint16_t port = gdefaultport)
	:_sockfd(gsockfd)
	,_addr(port)
	,_isrunning(false)
	{}

	void InitServer()  // 初始化服务器
	{
		// 1.创建套接字

		// int socket(int domain,int type,int protocol) domain 域/协议家族
		// 创建套接字会返回一个整数文件描述符   套接字本质就是文件
		_sockfd = ::socket(AF_INET,SOCK_DGRAM,0);
		if(_sockfd < 0)
		{
			LOG(LogLevel::FATAL) << "socket: " << strerror(errno);
			Die(SOCKET_ERR);
		}
		LOG(LogLevel::INFO) << "socket success,sockfd is: " << _sockfd;

		// 2.填充网络信息，并bind绑定
			
		// InetAddr 封装了，不用我们再填充信息

		// // 2.1 填充了结构体，并没有把socket信息设置进入内核
		// struct sockaddr_in local;
		// bzero(&local,sizeof(local)); // 先把结构体清零，再填充信息
		// local.sin_family = AF_INET;  // 地址族
		// local.sin_port = ::htons(_port);   // 端口号要被发送给对方的，即要发到网络中,htons主机转网络

		// 结构体可以被整体初始化，不能被整体赋值
		// //local.sin_addr.s_addr = ::inet_addr(_ip.c_str());  // 1.string->4bytes  2.network
				
		// local.sin_addr.s_addr = INADDR_ANY;
		// 服务器通常有多个网络接口（如以太网、无线网卡、虚拟网卡等）可能对应多个IP地址
		// 若显式绑定到某个具体IP,服务器只能接收发送到该 IP 的请求,用 INADDR_ANY，服务器能接收发送到本机所有 IP 的请求


		// 2.2 bind:设置进入内核
		// 将套接字与特定的 IP 地址和端口号绑定

		// int n = ::bind(_sockfd,(const struct sockaddr*)&local,sizeof(local))
		int n = ::bind(_sockfd,_addr.NetAddr(),_addr.NetAddrLen());
		if(n < 0)
		{
			LOG(LogLevel::FATAL) << "bind: " << strerror(errno);
			Die(BIND_ERR);
		}
		LOG(LogLevel::INFO) << "bind success";
	}  

	void Start()
	{
		_isrunning = true;
		while(1)
		{
			char inbuffer[1024];
			struct sockaddr_in peer;
			socklen_t len = sizeof(peer);

			// 接收数据报
			// ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,struct sockaddr *src_addr, socklen_t *addrlen);

			// 返回值：表示实际接收到的有效字节数   sizeof(inbuffer)-1 预留一个空间写\0
			ssize_t n = ::recvfrom(_sockfd,inbuffer,sizeof(inbuffer)-1,0,CONV(&peer),&len);  // 标志位0表示阻塞
			
			if(n > 0) // 成功
			{
				// 1.消息内容  &&   2.谁给我发的
				// uint16_t clientport = ::ntohs(peer.sin_port);      // 网络序列转主机序列
				// std::string clientip = ::inet_ntoa(peer.sin_addr); // 网络序列转主机序列，字符串风格的 network to ascii

				// 用传入的 sockaddr_in（peer 变量）初始化类成员 _net_addr，保存原始网络字节序的地址信息。
				InetAddr client(peer);
				inbuffer[n] = 0;      // 给接收的字节流数据手动添加字符串结束符,因为网络传输是字节流，而不是字符串
				std::string clientinfo = client.Ip() + ":" + std::to_string(client.Port()) + "# " + inbuffer;

				LOG(LogLevel::DEBUG)<< clientinfo;
			
				std::string echo_string = "echo#";
				echo_string += inbuffer;
				
				// 发消息
				// ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,const struct sockaddr *dest_addr, socklen_t addrlen);

				::sendto(_sockfd,echo_string.c_str(),echo_string.size(),0,CONV(&peer),sizeof(peer));
			}
		}
		_isrunning = false;
	}
	~UdpServer()
	{
		if(_sockfd > gsockfd)
			::close(_sockfd);
	}
private:
	int _sockfd;    // 既可以发，也可以收---全双工
	InetAddr _addr; 

	// uint16_t _port; // 服务器未来端口号
	//std::string _ip;// 服务器对应的IP
	bool _isrunning;// 服务器运行状态
};









#endif